// requestQueue.ts
import { ref, reactive, onUnmounted, getCurrentInstance } from 'vue';

/** -------------------
 * 基础配置
 * ------------------- */
const baseConfig = {
	get baseURL() {
		const ipAgreement = uni.getStorageSync("IPAgreement") || "";
		const serverIP = uni.getStorageSync("serverIP") || "";
		return ipAgreement + serverIP;
	},
	maxRetry: 1,
	retryDelay: 1000,
	unwrapData: false,
	apiPrefix: '/Frame/SqlData?cmdname='
};

/** -------------------
 * 类型定义
 * ------------------- */
interface RequestTask {
	id : string;
	url : string;
	method : 'GET' | 'POST' | 'PUT' | 'DELETE';
	data ?: any;
	config : RequestConfig;
	resolve : (value : any) => void;
	reject : (reason ?: any) => void;
	retryCount : number;
}

interface RequestConfig {
	baseURL ?: string;
	maxRetry ?: number;
	retryDelay ?: number;
	headers ?: RequestHeaders;
	isFullUrl ?: boolean;
	onSuccess ?: (response : any) => void;
	onError ?: (error : any) => void;
	unwrapData ?: boolean;
	apiPrefix ?: string;
	timeout ?: number;
	[key : string] : any;
}

interface RequestHeaders {
	[key : string] : string;
}

enum ErrorType {
	NETWORK_ERROR = 'NETWORK_ERROR',
	TIMEOUT = 'TIMEOUT',
	SERVER_ERROR = 'SERVER_ERROR',
	CLIENT_ERROR = 'CLIENT_ERROR',
}

/** -------------------
 * 工具函数
 * ------------------- */
function deepMerge(target : any, source : any) : any {
	const isObject = (obj : any) => obj && typeof obj === 'object';
	if (!isObject(target) || !isObject(source)) return source;

	Object.keys(source).forEach(key => {
		const t = target[key];
		const s = source[key];
		if (Array.isArray(t) && Array.isArray(s)) {
			target[key] = s;
		} else if (isObject(t) && isObject(s)) {
			target[key] = deepMerge(t, s);
		} else {
			target[key] = s;
		}
	});
	return target;
}

function getDefaultHeaders() : RequestHeaders {
	const headers : RequestHeaders = {
		'content-type': 'application/x-www-form-urlencoded;charset=utf-8'
	};
	const userInfo = uni.getStorageSync('UserInfo');
	if (userInfo) {
		headers['username'] = encodeURIComponent(JSON.parse(userInfo).UserName);
	}
	return headers;
}

function generateId() {
	return Math.random().toString(36).substr(2, 9);
}

/** -------------------
 * 请求队列管理器
 * ------------------- */
class RequestQueueManager {
	private queue : RequestTask[] = [];
	private runningTasks : Map<string, RequestTask> = new Map();
	private maxConcurrent = 5;
	private isDestroyed = false;

	// 防抖控制
	private allCompleteTimer : any = null;
	private lastAllCompleteAt = 0;
	private readonly MIN_ALLCOMPLETE_GAP = 1500;

	private stats = reactive({
		total: 0,
		completed: 0,
		failed: 0,
		pending: 0,
		active: 0,
	});

	private listeners = {
		start: [] as (() => void)[],
		eachComplete: [] as (() => void)[],
		allComplete: [] as (() => void)[],
		error: [] as ((err : any) => void)[],
	};

	addRequest(task : RequestTask) {
		if (this.isDestroyed) return;
		this.queue.push(task);
		this.stats.total++;
		this.stats.pending++;
		this.runNext();
	}

	private runNext() {
		if (this.isDestroyed) return;
		while (this.runningTasks.size < this.maxConcurrent && this.queue.length > 0) {
			const task = this.queue.shift()!;
			this.stats.pending--;
			this.stats.active++;
			this.runningTasks.set(task.id, task);

			this.listeners.start.forEach(cb => cb());
			this.executeRequest(task);
		}
	}

	private async executeRequest(task : RequestTask) {
		if (this.isDestroyed) return;
		const headers = deepMerge({ ...getDefaultHeaders() }, { ...task.config.headers });

		const { url, method, data = {}, config } = task;
		const apiPrefix = config.apiPrefix ?? baseConfig.apiPrefix;
		const useConfigBaseURL = typeof config.baseURL !== 'undefined';
		const fullUrl = config.isFullUrl
			? url
			: useConfigBaseURL
				? `${config.baseURL}${url}`
				: `${baseConfig.baseURL}${apiPrefix}${url}`;

		// 注入用户信息
		if (uni.getStorageSync('UserInfo')) {
			let u = JSON.parse(uni.getStorageSync('UserInfo'));
			Object.assign(data, {
				P_UserID: u.UserID,
				P_UserName: u.UserName,
				P_UserCode: u.UserCode,
				P_EmplID: u.EmplID,
				...data
			});
		}

		try {
			const response = await uni.request({
				url: "https://api.hzyc.org.cn/Handler.aspx",
				// url: "https://www.hzyc.org.cn/Handler.aspx",
				method,
				data: {
					...data,
					url: fullUrl
				},
				header: headers,
				timeout: config.timeout || 30000
			});

			const processed = this.processResponse(response, config);
			this.handleSuccess(task, processed);
		} catch (err) {
			this.handleError(task, err);
		}
	}

	private processResponse(response : any, config : RequestConfig) {
		const shouldUnwrap = config.unwrapData ?? baseConfig.unwrapData;
		if (shouldUnwrap &&
			response &&
			response.data &&
			typeof response.data === 'object' &&
			'data' in response.data) {
			return response.data.data;
		}
		return response.data || response;
	}

	private handleSuccess(task : RequestTask, response : any) {
		this.runningTasks.delete(task.id);
		this.stats.completed++;
		this.stats.active--;
		task.resolve(response);
		task.config.onSuccess?.(response);

		this.listeners.eachComplete.forEach(cb => cb());
		this.checkCompletion();
		this.runNext();
	}

	private handleError(task : RequestTask, error : any) {
		const maxRetry = task.config.maxRetry ?? baseConfig.maxRetry;
		if (task.retryCount < maxRetry) {
			task.retryCount++;
			setTimeout(() => {
				this.queue.unshift(task);
				this.stats.pending++;
				this.runNext();
			}, task.config.retryDelay ?? baseConfig.retryDelay);
			return;
		}

		this.runningTasks.delete(task.id);
		this.stats.failed++;
		this.stats.active--;

		const errorInfo = { error, task, type: this.getErrorType(error) };
		task.reject(errorInfo);
		task.config.onError?.(errorInfo);
		this.listeners.error.forEach(cb => cb(errorInfo));

		this.listeners.eachComplete.forEach(cb => cb());
		this.checkCompletion();
		this.runNext();
	}

	private getErrorType(error : any) : ErrorType {
		if (error.errMsg?.includes('timeout')) return ErrorType.TIMEOUT;
		if (error.errMsg?.includes('network')) return ErrorType.NETWORK_ERROR;
		if (error.statusCode >= 500) return ErrorType.SERVER_ERROR;
		if (error.statusCode >= 400) return ErrorType.CLIENT_ERROR;
		return ErrorType.NETWORK_ERROR;
	}

	private checkCompletion() {
		if (this.runningTasks.size === 0 && this.queue.length === 0) {
			clearTimeout(this.allCompleteTimer);
			const now = Date.now();
			const wait = Math.max(0, this.MIN_ALLCOMPLETE_GAP - (now - this.lastAllCompleteAt));
			this.allCompleteTimer = setTimeout(() => {
				this.lastAllCompleteAt = Date.now();
				this.listeners.allComplete.forEach(cb => cb());
			}, wait);
		}
	}

	// 注册事件
	onStart(cb : () => void) { this.listeners.start.push(cb); }
	onEachComplete(cb : () => void) { this.listeners.eachComplete.push(cb); }
	onAllComplete(cb : () => void) { this.listeners.allComplete.push(cb); }
	onError(cb : (err : any) => void) { this.listeners.error.push(cb); }

	// 管理方法
	cancel(taskId : string) {
		const before = this.queue.length;
		this.queue = this.queue.filter(t => t.id !== taskId);
		return before !== this.queue.length;
	}

	clear() {
		const count = this.queue.length;
		this.queue = [];
		return count;
	}

	status() {
		return this.stats;
	}

	destroy() {
		this.isDestroyed = true;
		this.queue = [];
		this.runningTasks.clear();
		Object.keys(this.listeners).forEach(k => (this.listeners as any)[k] = []);
	}
}

/** -------------------
 * Vue Hook 封装（支持全局模式）
 * ------------------- */
const requestInstances = new Map();

function useRequestQueue(forceGlobal = false) {
	const instance = getCurrentInstance();
	// 没有 Vue 实例，或者强制使用全局模式时，统一走 global
	const instanceId = forceGlobal || !instance
		? 'global'
		: instance.uid.toString();

	let requestQueue = requestInstances.get(instanceId);
	if (!requestQueue) {
		requestQueue = new RequestQueueManager();
		requestInstances.set(instanceId, requestQueue);
	}

	// 如果是组件实例，卸载时销毁
	if (instance && !forceGlobal) {
		onUnmounted(() => {
			requestQueue?.destroy();
			requestInstances.delete(instanceId);
		});
	}

	// 封装请求方法
	const request = <T = any>(options : any) => new Promise<T>((resolve, reject) => {
		const task : RequestTask = {
			id: generateId(),
			url: options.url,
			method: options.method,
			data: options.data,
			config: { ...options.config },
			resolve: (res) => { options.success?.(res); resolve(res); },
			reject: (err) => { options.fail?.(err); reject(err); },
			retryCount: 0
		};
		requestQueue!.addRequest(task);
	});

	const requestByUrl = <T = any>(options : any) => new Promise<T>((resolve, reject) => {
		let baseURL = options.config?.baseURL || '';
		if (!baseURL.includes('http')) {
			const serverIP = uni.getStorageSync('serverIP') || '';
			const IPAgreement = uni.getStorageSync("IPAgreement") || "http://";
			baseURL = `${IPAgreement}${serverIP}${baseURL}`;
		}
		const task : RequestTask = {
			id: generateId(),
			url: options.url,
			method: options.method,
			data: options.data,
			config: { ...options.config, baseURL },
			resolve: (res) => {
				if (res.status === 500) {
					console.log(res.msg);
				}
				options.success?.(res);
				resolve(res);
			},
			reject: (err) => {
				console.log(err);
				options.fail?.(err);
				reject(err);
			},
			retryCount: 0
		};
		requestQueue!.addRequest(task);
	});

	const PromiseRequest = <T = any>(options : any) => new Promise<T>((resolve, reject) => {
		const task : RequestTask = {
			id: generateId(),
			url: options.url,
			method: options.method,
			data: options.data,
			config: { ...options.config },
			resolve,
			reject,
			retryCount: 0
		};
		requestQueue!.addRequest(task);
	});

	const PromiseRequestByUrl = <T = any>(options : any) => new Promise<T>((resolve, reject) => {
		let baseURL = options.config?.baseURL || '';
		if (!baseURL.includes('http')) {
			const serverIP = uni.getStorageSync('serverIP') || '';
			const IPAgreement = uni.getStorageSync("IPAgreement") || "http://";
			baseURL = `${IPAgreement}${serverIP}${baseURL}`;
		}
		const task : RequestTask = {
			id: generateId(),
			url: options.url,
			method: options.method,
			data: options.data,
			config: { ...options.config, baseURL },
			resolve,
			reject,
			retryCount: 0
		};
		requestQueue!.addRequest(task);
	});

	return {
		request,
		requestByUrl,
		PromiseRequest,
		PromiseRequestByUrl,
		onStart: requestQueue.onStart.bind(requestQueue),
		onEachComplete: requestQueue.onEachComplete.bind(requestQueue),
		onAllComplete: requestQueue.onAllComplete.bind(requestQueue),
		onError: requestQueue.onError.bind(requestQueue),
		cancel: requestQueue.cancel.bind(requestQueue),
		clear: requestQueue.clear.bind(requestQueue),
		status: requestQueue.status.bind(requestQueue),
	};
}

export default useRequestQueue;